package com.hw.protocol.tool.util;

/**
 * @author : lihy
 * @date : 2023/3/10 16:00
 * description: crc校验工具
 */

public class CrcUtil {

    /**
     * 计算CRC16校验码
     *
     * @param bytes    crc数组
     * @param exchange 是否需要高低位互换
     * @return int类型校验码
     */
    public static int getCrcInteger(byte[] bytes, boolean exchange) {
        int crc = 0x0000ffff;
        int polynomial = 0x0000a001;

        int i, j;
        for (i = 0; i < bytes.length; i++) {
            crc ^= ((int) bytes[i] & 0x000000ff);
            for (j = 0; j < 8; j++) {
                if ((crc & 0x00000001) != 0) {
                    crc >>= 1;
                    crc ^= polynomial;
                } else {
                    crc >>= 1;
                }
            }
        }
        if (exchange) {
            crc = ((crc & 0xff00) >> 8) | ((crc & 0x00ff) << 8);
        }
        return crc;
    }

    /**
     * 计算CRC16校验码
     *
     * @param bytes    crc数组
     * @param exchange 是否需要高低位互换
     * @return byte数组类型校验码
     */
    public static byte[] getCrcBytes(byte[] bytes, boolean exchange) {
        int crc = getCrcInteger(bytes, exchange);
        return new byte[]{(byte) ((crc & 0xff00) >> 8), (byte) (crc & 0x00ff)};
    }

    /**
     * 计算CRC16校验码
     *
     * @param bytes    crc数组
     * @param exchange 是否需要高低位互换
     * @return 字符串类型校验码
     */
    public static String getCrc(byte[] bytes, boolean exchange) {
        int crc = getCrcInteger(bytes, exchange);
        return String.format("%04X", crc);
    }

    /**
     * 计算CRC16校验码(xmodem算法)
     *
     * @param bytes    crc数组
     * @param exchange 是否需要高低位互换
     * @return int类型校验码
     */
    public static int getCrc16XmodemInteger(byte[] bytes, boolean exchange) {
        int crc = 0x0000;
        int polynomial = 0x1021;

        for (byte b : bytes) {
            for (int i = 0; i < 8; i++) {
                boolean bit = ((b >> (7 - i) & 1) == 1);
                boolean c15 = ((crc >> 15 & 1) == 1);
                crc <<= 1;
                if (c15 ^ bit) {
                    crc ^= polynomial;
                }
            }
        }
        crc &= 0xffff;
        if (exchange) {
            crc = ((crc & 0xff00) >> 8) | ((crc & 0x00ff) << 8);
        }
        return crc;

    }


    /**
     * 计算CRC16校验码(xmodem算法)
     *
     * @param bytes    crc数组
     * @param exchange 是否需要高低位互换
     * @return 字符串类型校验码
     */
    public static String getCrc16Xmodem(byte[] bytes, boolean exchange) {
        int crc = getCrc16XmodemInteger(bytes, exchange);
        return String.format("%04X", crc);
    }

    /**
     * 计算CRC16校验码(xmodem算法)
     *
     * @param bytes    crc数组
     * @param exchange 是否需要高低位互换
     * @return 字节数组类型校验码
     */
    public static byte[] getCrc16XmodemBytes(byte[] bytes, boolean exchange) {
        int crc = getCrc16XmodemInteger(bytes, exchange);
        return new byte[]{(byte) ((crc & 0xff00) >> 8), (byte) (crc & 0x00ff)};
    }

    /**
     * 十六进制的字符串转换为byte数组
     *
     * @param hex16Str 十六进制字符串
     * @return byte数组
     */
    public static byte[] convert16HexToByte(String hex16Str) {
        char[] c = hex16Str.toUpperCase().toCharArray();
        byte[] b = new byte[c.length / 2];
        for (int i = 0; i < b.length; i++) {
            int pos = i * 2;
            b[i] = (byte) ("0123456789ABCDEF".indexOf(c[pos]) << 4 | "0123456789ABCDEF"
                    .indexOf(c[pos + 1]));
        }
        return b;
    }

    /**
     * 字节数组转16进制
     *
     * @param bytes    需要转换的byte数组
     * @param addBlank 是否需要空格
     * @return 转换后的Hex字符串
     */
    public static String bytesToHex(byte[] bytes, boolean addBlank) {
        StringBuilder sb = new StringBuilder();
        for (byte aByte : bytes) {
            String hex = Integer.toHexString(aByte & 0xFF);
            if (hex.length() < 2) {
                sb.append(0);
            }
            sb.append(hex);
            if (addBlank) {
                sb.append(" ");
            }
        }
        return sb.toString().toUpperCase();
    }

    /**
     * 字节数组转16进制（不包含空格）
     *
     * @param bytes 需要转换的byte数组
     * @return 转换后的Hex字符串
     */
    public static String bytesToHex(byte[] bytes) {
        return bytesToHex(bytes, false);
    }


    /**
     * CRC校验码
     *
     * @param hex16Str 十六进制字符串
     * @param exchange 是否需要高低位互换
     * @return CRC校验码
     */
    public static String makeCrc(String hex16Str, boolean exchange) {
        byte[] bytes = convert16HexToByte(hex16Str);
        return getCrc(bytes, exchange);
    }

    /**
     * 校验crc码
     *
     * @param noCrcData 协议校验的数据
     * @param exchange  是否需要高低位互换
     * @param crc       数据的crc码
     * @return
     */
    public static boolean checkCrc(byte[] noCrcData, boolean exchange, byte[] crc) {
        byte[] calCrc = getCrcBytes(noCrcData, exchange);
        return calCrc[0] == crc[0] && calCrc[1] == crc[1];
    }

    /**
     * 校验crc码
     *
     * @param noCrcData 协议校验的数据
     * @param exchange  是否需要高低位互换
     * @param crc       数据的crc码
     * @return
     */
    public static boolean checkCrc(byte[] noCrcData, boolean exchange, int crc) {
        int calCrc = getCrcInteger(noCrcData, exchange);
        return calCrc == crc;
    }

    /**
     * 校验crc码(crc16-xmodem)
     *
     * @param noCrcData 协议校验的数据
     * @param exchange  是否需要高低位互换
     * @param crc       数据的crc码
     * @return
     */
    public static boolean chechCrc16Xmodem(byte[] noCrcData, boolean exchange, byte[] crc) {
        byte[] calCrc = getCrc16XmodemBytes(noCrcData, exchange);
        return calCrc[0] == crc[0] && calCrc[1] == crc[1];
    }
}
